#include "ServerAhorcado.h"
#include <stdio.h>
#include <string.h>
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include "BitStream.h"
#include "RakNetTypes.h"  // MessageID
#include "GameMessageId.h"
#include <iostream>
#include "inLobbyClients.h"
#include "HandleInput.h"
#include <conio.h>
#include "ServerConfigs.h"
#include "GameMessage.h"

#define MAX_CLIENTS 10

bool endServer = false;

ServerAhorcado::ServerAhorcado():
correctLetters(NULL),
incorrectLetters(NULL),
numPlayers(0),
peer( RakNet::RakPeerInterface::GetInstance()),
sd(RakNet::SocketDescriptor(SERVER_PORT,0)){
}

ServerAhorcado::~ServerAhorcado(){
	if(peer!=NULL)
		RakNet::RakPeerInterface::DestroyInstance(peer);
}

bool ServerAhorcado::startServer()
{	
	RakNet::Packet *packet;
	
	//Select the word...
	do{
		selectWinningWord();
	}while(!validateWord());
	
	//Start the server
	configurateServer();

	//Lobby State
	lobbyState();
	if(endServer)
		return false;

	//Game State
	startGame();

	return true;
}

void ServerAhorcado::lobbyState(){
	bool stayInLobby=true;
	InLobbyClients *lobbyClients = new InLobbyClients();
	RakNet::Packet *packet;
	int portListening = SERVER_PORT;
	
	while(stayInLobby){
		unsigned short connectionCount;
		bool b = peer->GetConnectionList (NULL, &connectionCount);
		numPlayers = (b)?(int)connectionCount:numPlayers;
		
		for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
		{
			if(packet && !isGameMessageIdValid(packet->data[0]))
				continue; //ignore crap

			GameMessage gm(packet->data);
			RakNet::RakString rs;
			RakNet::BitStream bsIn(packet->data,packet->length,false);
			bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
			bsIn.Read(rs);
			switch (gm.getMessageId())
			{
			case ID_NEW_INCOMING_CONNECTION:
				lobbyClients->connections[packet->systemAddress] = GameMessageId::LOBBY_PLAYER_NOT_READY;
				break;
			case LOBBY_PLAYER_READY:  
			case LOBBY_PLAYER_NOT_READY:
				lobbyClients->connections[packet->systemAddress] = (GameMessageId)packet->data[0];
				lobbyClients->playersNames[rs.C_String()] = packet->systemAddress;
				break;
			case ID_CONNECTION_LOST:
				lobbyClients->connections.erase(packet->systemAddress);
			default:
				lobbyClients->connections[packet->systemAddress] = GameMessageId::LOBBY_PLAYER_NOT_READY;
			}
		}
		lobbyScreenWriter(lobbyClients);
		if(HandleInput::getCharAsynchronously()==13){
			stayInLobby = !validateToStartGame(lobbyClients);
		}
		if(exitCheck())
		{
			endServer = true;
			break;
		}
	}
}

void ServerAhorcado::configurateServer(){
	peer->Startup(MAX_CLIENTS,&sd,1);
	peer->SetMaximumIncomingConnections(MAX_CLIENTS);
}

void ServerAhorcado::lobbyScreenWriter(InLobbyClients *lc){
	static DWORD i;
	if(i-GetTickCount()>500){
		i = GetTickCount();
		system("cls");
		printf("You are in the Lobby\nPress Enter to start the game once every player is ready...\n");
		printf("Players(%d):\n", lc->connections.size());
		for(std::map<std::string,RakNet::SystemAddress>::iterator it = lc->playersNames.begin(); it!=lc->playersNames.end();++it)
		{
			RakNet::SystemAddress address = it->second;
			GameMessageId gmi = lc->connections[address];
			std::string name = it->first;
			printf("%s is %s\n",name.c_str(),((gmi==GameMessageId::LOBBY_PLAYER_READY)?"Ready":"Not Ready"));
		}
		/*
		for(InLobbyClientsIterator it = lc->connections.begin(); it!= lc->connections.end(); ++it){
			//printf("%s is %s\n",it->first.ToString(),(it->second==GameMessageId::LOBBY_PLAYER_READY)?"Ready":"Not Ready");
			std::string uName = playersNames[it->first]; //If it has a name already
			printf("%s is %s\n",(uName.compare("")==0)?it->first.ToString():playersNames[it->first].c_str(),(it->second==GameMessageId::LOBBY_PLAYER_READY)?"Ready":"Not Ready");
		}*/
	}

}


void ServerAhorcado::startGame(){
	initializeGameAttributes();
	while(1){
		managePackages();
		update();
		if(exitCheck())
			return;
	}
}

void ServerAhorcado::initializeGameAttributes(){
}

void ServerAhorcado::managePackages(){
	RakNet::Packet *packet;// = NULL;
	GameMessage* gm = NULL;
	for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive()){
		switch (packet->data[0]){
		default:
			break;
		}
	}
}

void ServerAhorcado::update(){

}

bool ServerAhorcado::validateWord(){
	bool ans = false;
	if(this->winningWord.size()!=0)
		ans = true;
	return ans;
}

bool ServerAhorcado::validateToStartGame(InLobbyClients *lc){
	bool ans = lc->connections.size()!=0;
	for(InLobbyClientsIterator it = lc->connections.begin(); it!= lc->connections.end(); ++it){
		if( it->second != GameMessageId::LOBBY_PLAYER_READY) {
			ans = false;
		}
	}
	return ans;
}


void ServerAhorcado::selectWinningWord(){
	std::cout << "Elija la palabra ganadora: ";
	std::cin >> winningWord;
	RakNet::SystemAddress* pkConections = NULL;
}

bool ServerAhorcado::isWordCompletted(){
	bool ans = false;

	return ans;
}

bool ServerAhorcado::checkLetter(char c){
	bool ans = false;

	return ans;
}



/*
	char str[512];
	bool startGame;

	RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
	RakNet::Packet *packet;
	
	int numPlayers = 0;

	printf("Starting the server.\n");
	
	// We need to let the server accept incoming connections from the clients
	peer->SetMaximumIncomingConnections(MAX_CLIENTS);
	printf("Insert winning word:");
	str[0] = '\0';
	gets(str);
	printf("The winning word is: %s\n", str);
	printf("Waiting for players...\n");
	str[0] = '\0';

	while (!startGame)
	{
		for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
		{
			if (packet->data[0] == ID_NEW_INCOMING_CONNECTION)
			{
				numPlayers++;
				printf("%i players available, waiting for %i more...\n", numPlayers, 2 - numPlayers);
			}
		}
	}
	printf("GAME STARTED!!!\n");

	RakNet::SystemAddress* pkConections = NULL;
	unsigned short connectionCount = 0;
	bool b = peer->GetConnectionList (NULL, &connectionCount);

	if (b)
	{
		pkConections = new RakNet::SystemAddress[connectionCount];
		peer->GetConnectionList (pkConections, &connectionCount);
	}

	while (1)
	{
		for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
		{
			switch (packet->data[0])
			{
			case ID_REMOTE_DISCONNECTION_NOTIFICATION:
				printf("Another client has disconnected.\n");
				break;
			case ID_REMOTE_CONNECTION_LOST:
				printf("Another client has lost the connection.\n");
				break;
			case ID_REMOTE_NEW_INCOMING_CONNECTION:
				printf("Another client has connected.\n");
				break;
			case ID_CONNECTION_REQUEST_ACCEPTED:
				{
					printf("Our connection request has been accepted.\n");

					// Use a BitStream to write a custom user message
					// Bitstreams are easier to use than sending casted structures, and handle endian swapping automatically
					RakNet::BitStream bsOut;
					bsOut.Write((RakNet::MessageID)ID_GAME_MESSAGE_1);
					bsOut.Write("Hello world");
					peer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
				}
				break;
			case ID_NEW_INCOMING_CONNECTION:
				printf("A connection is incoming.\n");
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				printf("The server is full.\n");
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				printf("A client has disconnected.\n");
				break;
			case ID_CONNECTION_LOST:
				printf("A client lost the connection.\n");
				break;
				
			case ID_GAME_MESSAGE_1:
				{
					RakNet::RakString rs;
					RakNet::BitStream bsIn(packet->data,packet->length,false);
					bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
					bsIn.Read(rs);
					printf("%s\n", rs.C_String());
				}
				break;
			
			default:
				printf("Message with identifier %i has arrived.\n", packet->data[0]);
				break;
			}
		}
	}


	RakNet::RakPeerInterface::DestroyInstance(peer);

	if (pkConections)
	{
		delete[] pkConections;
		pkConections = NULL;
	}
	return true;*/